Number.prototype.isZero = function () {
  return 0 === Number(this);
}

class Point {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y
  }
}

class LinkSearch {
  /**
   * 0折点连通：两点之间直线连通
   * 连通：元素与元素之间的元素为空
   * @param {Number[][]} data 游戏棋盘的映射
   * @param {Point} srcPoint 起始点
   * @param {Point} destPoint 终点
   * @return {boolean} 两者间元素为空则返回true
   */
  static lineConnection(data, srcPoint, destPoint) {
    // 不属于0折点（直线）连接
    if (srcPoint.x !== destPoint.x && srcPoint.y !== destPoint.y) {
      return false;
    }

    // 两点x坐标相同，在水平方向匹配
    if (srcPoint.x === destPoint.x) {
      let min = Math.min(srcPoint.y, destPoint.y);
      let max = Math.max(srcPoint.y, destPoint.y);
      // 水平方向上，遍历 min 到 max 之间的元素
      for (let i = min + 1; i < max; i++) {
        if (!data[srcPoint.x][i].isZero()) {
          return false;
        }
      }
    }
    // 两点y坐标相同，在垂直方向匹配
    else {
      let min = Math.min(srcPoint.x, destPoint.x);
      let max = Math.max(srcPoint.x, destPoint.x);
      // 垂直方向上，遍历 min 到 max 之间的元素
      for (let i = min + 1; i < max; i++) {
        // 只要有一个元素非空则不是0折连通
        if (!data[i][srcPoint.y].isZero()) {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * 1折点连通：两点形成的矩形的对角线的点至少有一个与这两个点是直线连通：两点形成的直角路径有一个是连通的
   * @param {Number[][]} data 游戏棋盘的映射
   * @param {Point} srcPoint 起始点
   * @param {Point} destPoint 终点
   * @return {Point|null} 如果存在这样的点则返回它，否则返回null
   */
  static brokenLineConnectionOnce(data, srcPoint, destPoint) {
    // 非1折点连通
    if (srcPoint.x === destPoint.x || srcPoint.y === destPoint.y) {
      return null;
    }

    // 矩形对角点
    const point1 = new Point(srcPoint.x, destPoint.y);
    const point2 = new Point(destPoint.x, srcPoint.y);

    if (data[point1.x][point1.y].isZero()) {
      const isLink1 = LinkSearch.lineConnection(data, srcPoint, point1);
      const isLink2 = isLink1 ? LinkSearch.lineConnection(data, point1, destPoint) : isLink1;
      if (isLink1 && isLink2)
        return point1;
    }

    if (data[point2.x][point2.y].isZero()) {
      const isLink1 = LinkSearch.lineConnection(data, srcPoint, point2);
      const isLink2 = isLink1 ? LinkSearch.lineConnection(data, point2, destPoint) : isLink1;
      if (isLink1 && isLink2)
        return point2;
    }

    return null;
  }

  /**
   * 2折点连接
   * @param {Number[][]} data
   * @param {Point} srcPoint
   * @param {Point} destPoint
   * @return {Point[]|null}
   */
  static brokenLineConnectionTwice(data, srcPoint, destPoint) {
    for (let i = srcPoint.y + 1; i < data[srcPoint.x].length; i++) {
      if (!data[srcPoint.x][i].isZero())
        break;
      // 查找一个点和destPoint是否存在1折点连通
      const src = new Point(srcPoint.x, i);
      const dest = LinkSearch.brokenLineConnectionOnce(data, src, destPoint)
      if (dest !== null)
        return [src, dest];
    }

    for (let i = srcPoint.y - 1; i > -1; i--) {
      if (!data[srcPoint.x][i].isZero())
        break;
      const src = new Point(srcPoint.x, i);
      const dest = LinkSearch.brokenLineConnectionOnce(data, src, destPoint)
      if (dest !== null)
        return [src, dest];
    }

    for (let i = srcPoint.x + 1; i < data.length; i++) {
      if (!data[i][srcPoint.y].isZero())
        break;
      const src = new Point(i, srcPoint.y);
      const dest = LinkSearch.brokenLineConnectionOnce(data, src, destPoint)
      if (dest !== null)
        return [src, dest];
    }

    for (let i = srcPoint.x - 1; i > -1; i--) {
      if (!data[i][srcPoint.y].isZero())
        break;
      const src = new Point(i, srcPoint.y);
      const dest = LinkSearch.brokenLineConnectionOnce(data, src, destPoint)
      if (dest !== null)
        return [src, dest];
    }

    return null;
  }

  /**
   * 判断是否连通：3种情况都要判断
   * @param {Number[][]} data
   * @param {Point} srcPoint
   * @param {Point} destPoint
   * @return {Point[]|null} null: 没有连通 []: 直线连通 [].length===1: 1折连通 [].length===2: 2折连通
   */
  static connection(data, srcPoint, destPoint) {
    // 0折点
    if (LinkSearch.lineConnection(data, srcPoint, destPoint))
      return [];

    // 1折点
    const point = LinkSearch.brokenLineConnectionOnce(data, srcPoint, destPoint);
    if (point !== null)
      return [point];

    // 2折点
    return LinkSearch.brokenLineConnectionTwice(data, srcPoint, destPoint);
  }
}

export default LinkSearch
